/**
    Copyright (c) 2010-2011 yakiimo02
    Distributed under the New BSD License.
    See included license.txt or http://www.yakiimo3d.com/NewBSDLicense.txt
**/

#include "DXUT.h"
#include "MandelbrotMgr.h"

MandelbrotMgr    g_mandelbrotMgr;

MandelbrotMgr::MandelbrotMgr()
{
    m_pActiveMandelbrot = NULL;
    SetMode( MANDELBROT_MODE_DIRECTCOMPUTE );
}

MandelbrotMgr::~MandelbrotMgr()
{
}

HRESULT MandelbrotMgr::OnCreateDevice( ID3D11Device* pd3dDevice )
{
    HRESULT hr = S_OK;
    V_RETURN( m_directCompute.OnCreateDevice( pd3dDevice ) );
    return S_OK;
}

HRESULT MandelbrotMgr::OnResizedSwapChain( ID3D11Device* pd3dDevice, const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc )
{
    HRESULT hr = S_OK;

    // recreate mandelbrot databuffer to match screen back buffer dimensions
    V_RETURN( m_dataBufferDX11.OnResizedSwapChain( pd3dDevice, pBackBufferSurfaceDesc ) );

    V_RETURN( m_directCompute.OnResizedSwapChain( pd3dDevice, &m_dataBufferDX11 ) );
    V_RETURN( m_cuda.OnResizedSwapChain( pd3dDevice, &m_dataBufferDX11 ) );    
    m_cpu.Create();
    
    return S_OK;
}

void MandelbrotMgr::OnFrameRender( ID3D11Device* pd3dDevice, ID3D11DeviceContext* pd3dImmediateContext,
                      double fTime, float fElapsedTime, void* pUserContext )
{
    m_pActiveMandelbrot->OnFrameRender( pd3dDevice, pd3dImmediateContext,fTime, fElapsedTime, &m_dataBufferDX11 );
}

void MandelbrotMgr::OnDestroyDevice()
{
    m_cuda.OnDestroyDevice();
    m_directCompute.OnDestroyDevice();
    m_cpu.Destroy();
    m_dataBufferDX11.Destroy();
}

void MandelbrotMgr::TranslateCenter( const D3DXVECTOR2& vDeltaCenter )
{
    m_cuda.TranslateCenter( vDeltaCenter );
    m_directCompute.TranslateCenter( vDeltaCenter );
    m_cpu.TranslateCenter( vDeltaCenter );
}

void MandelbrotMgr::ZoomScale( const float fDeltaScale )
{
    m_cuda.ZoomScale( fDeltaScale );
    m_directCompute.ZoomScale( fDeltaScale );
    m_cpu.ZoomScale( fDeltaScale );
}

void MandelbrotMgr::SetnMaxIterations( int nMaxIterations )
{
    m_cuda.SetnMaxIterations( nMaxIterations );
    m_directCompute.SetnMaxIterations( nMaxIterations );
    m_cpu.SetnMaxIterations( nMaxIterations );
}

LRESULT MandelbrotMgr::HandleMessages( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    // Current mouse position
    int nMouseX = ( short )LOWORD( lParam );
    int nMouseY = ( short )HIWORD( lParam );
    D3DXVECTOR2 vPosNow = D3DXVECTOR2( static_cast< float >( nMouseX ), static_cast< float >( nMouseY ) );

    static D3DXVECTOR2 vPosPrevL;
    static D3DXVECTOR2 vPosPrevR;

    //float fFPS = DXUTGetFPS();
    //if( fFPS < 1.0f ) { fFPS = 60.0f; }
    float fFPS = 60.0f;

    switch( uMsg )
    {
        case WM_LBUTTONDOWN:
        case WM_LBUTTONDBLCLK:
            vPosPrevL = vPosNow;
            return TRUE;

        case WM_LBUTTONUP:
            return TRUE;

        case WM_RBUTTONDOWN:
        case WM_RBUTTONDBLCLK:
            vPosPrevR = vPosNow;
            return TRUE;

        case WM_RBUTTONUP:
        case WM_MBUTTONUP:
            return TRUE;

        case WM_MOUSEMOVE:
            if( ( MK_LBUTTON & wParam ) && ::GetKeyState( VK_LSHIFT ) & 0x80 )
            {
                const float POS_DELTA = 0.005f;
                D3DXVECTOR2 vPosDiff = vPosNow - vPosPrevL;
                D3DXVECTOR2 vPosDiffScaled = vPosDiff * POS_DELTA * ( 60.0f / fFPS );
                vPosDiffScaled.x = -vPosDiffScaled.x;
                TranslateCenter( vPosDiffScaled );
                vPosPrevL = vPosNow;
            }
            if( ( MK_RBUTTON & wParam ) )
            {
                const float ZOOM_DELTA = 0.01f;
                float fPosDiff = ( vPosNow.x - vPosPrevR.x ) * ZOOM_DELTA;
                float fPosDiffScaled = 1.0f - fPosDiff * ( 60.0f / fFPS );
                if( fPosDiffScaled < 0.0f ) {
                    fPosDiffScaled  = 1.0f / abs( fPosDiffScaled );
                }
                ZoomScale( fPosDiffScaled );
                vPosPrevR = vPosNow;                
            }
            return TRUE;
    }

    return FALSE;    
}